package com.cloud.tsp.service.impl;

import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cloud.api.annotation.LocalLock;
import com.cloud.api.domain.alarm.*;
import com.cloud.api.domain.report.QueryAlarmReport;
import com.cloud.api.domain.report.QueryAlarms;
import com.cloud.api.domain.report.VehicleAlarmReport;
import com.cloud.api.domain.screen.AlarmList;
import com.cloud.api.domain.screen.RealTimeAlarm;
import com.cloud.api.domain.screen.VehicleBreakdown;
import com.cloud.api.domain.tsp.VehicleSale;
import com.cloud.api.myenum.AlarmLevelEnum;
import com.cloud.api.myenum.AlarmTypeEnum;
import com.cloud.api.utils.ExcelUtils;
import com.cloud.core.exception.MyException;
import com.cloud.mybatis.domain.TableDataInfo;
import com.cloud.tsp.mapper.VehicleAlarmMapper;
import com.cloud.tsp.service.EmailService;
import com.cloud.tsp.service.IVehicleAlarmService;
import com.cloud.tsp.service.IVehicleSaleService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Service;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.YearMonth;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;

/**
 * <p>
 * OLAP 服务实现类
 * </p>
 *
 * @author haohaibo
 * @since 2024-01-23
 */
@Service("vehicleAlarmService")
public class VehicleAlarmServiceImpl extends ServiceImpl<VehicleAlarmMapper, VehicleAlarm> implements IVehicleAlarmService {

    @Resource
    private VehicleAlarmMapper vehicleAlarmMapper;
    @Resource
    private MessageSource messageSource;
    @Resource
    private IVehicleSaleService vehicleSaleService;

    @Autowired
    private EmailService emailService;

    @Override
    public TableDataInfo<VehicleAlarm> selectVehicleAlarmList(QueryAlarm para) {
        Page<VehicleAlarm> page = new Page<>();
        page.setCurrent(para.getPage());
        page.setSize(para.getPageSize());
        /*long offset = para.getPage();
        long size = para.getPageSize();
        if (offset <= 1L) {
            offset =  0L;
        }else {
            offset = Math.max((offset - 1) * size, 0L);
        }*/
        if (StringUtil.isBlank(para.getLicenseNumber())){
            IPage<VehicleAlarm> data = vehicleAlarmMapper.selectVehicleAlarmList(page, para);
            parseVin(data.getRecords());
            return TableDataInfo.build(data);
        }

        VehicleSale one = vehicleSaleService.getOne(new LambdaQueryWrapper<VehicleSale>().eq(VehicleSale::getLicenseNumber, para.getLicenseNumber()));
        if (one == null){
            return TableDataInfo.build();
        }
        para.setVin(one.getVin());
        IPage<VehicleAlarm> data = vehicleAlarmMapper.selectVehicleAlarmList(page, para);
        parseVin(data.getRecords());
        return TableDataInfo.build(data);

//        List<VehicleAlarm> data = vehicleAlarmMapper.selectVehicleAlarmList(offset,size, para);
        /*if (data.isEmpty()) {
            return TableDataInfo.build();
        }
        for (VehicleAlarm record : data) {
            record.setVin(StrUtil.hide(record.getVin(), 8, 11));
            if (StringUtil.isNotBlank(record.getLicenseNumber())){
                if ("oil".equals(record.getVehicleType())){
                    record.setLicenseNumber(StrUtil.hide(record.getLicenseNumber(), 3, 6));
                }else {
                    record.setLicenseNumber(StrUtil.hide(record.getLicenseNumber(), 4, 7));
                }
            }
        }
        return TableDataInfo.build(data);*/
    }

    @Override
    public AlarmDetail selectVehicleDetail(String id) {
        VehicleAlarm vehicleAlarm = vehicleAlarmMapper.selectById(id);
        if (vehicleAlarm != null){
            VehicleSale one = vehicleSaleService.getOne(new LambdaQueryWrapper<VehicleSale>()
                    .eq(VehicleSale::getVin, vehicleAlarm.getVin())
                    .select(VehicleSale::getLicenseNumber));
            AlarmDetail detail = new AlarmDetail();
            detail.setVin(vehicleAlarm.getVin());
            detail.setLicenseNumber(one.getLicenseNumber());
            detail.setAlarmLevel(AlarmLevelEnum.getDesc(vehicleAlarm.getAlarmLevel().shortValue()));
            detail.setAlarmType(vehicleAlarm.getAlarmType());
            detail.setAlarmAddress(vehicleAlarm.getAlarmAddress());
            detail.setAlarmTime(vehicleAlarm.getAlarmTime());
            return detail;
        }
        return null;
    }

    private void parseVin(List<VehicleAlarm> data){
        for (VehicleAlarm record : data) {
            record.setVin(StrUtil.hide(record.getVin(), 8, 11));
        }
    }

    @Override
    public VehicleAlarm selectVehicleAlarmDetail(String id) {
        return vehicleAlarmMapper.selectVehicleAlarmDetail(id);
    }

    @Override
    public List<VehicleAlarm> selectAlarmListByVin(String id) {
        return vehicleAlarmMapper.selectAlarmListByVin(id);
    }

    @Override
    public void handleAlarm(VehicleAlarm para) {
        /*LambdaQueryWrapper<VehicleAlarm> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(VehicleAlarm::getVin,para.getVin());
        queryWrapper.eq(VehicleAlarm::getAlarmTime,para.getAlarmTime());
        VehicleAlarm data = vehicleAlarmMapper.selectOne(queryWrapper);*/

        VehicleAlarm data = vehicleAlarmMapper.selectById(para.getId());
        if (data.getStatus() == 1){
            throw new MyException("wds.exception.alarmHasHandle");
        }

        /*LambdaUpdateWrapper<VehicleAlarm> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(VehicleAlarm::getVin,para.getVin())
                .eq(VehicleAlarm::getAlarmTime,para.getAlarmTime());*/

        VehicleAlarm vehicleAlarm = new VehicleAlarm();
        vehicleAlarm.setId(para.getId());
        vehicleAlarm.setHandler(para.getHandler());
        vehicleAlarm.setPhoneNumber(para.getPhoneNumber());
        vehicleAlarm.setAlarmReason(para.getAlarmReason());
        vehicleAlarm.setSuggestHandleMethod(para.getSuggestHandleMethod());
        vehicleAlarm.setRemark(para.getRemark());
        vehicleAlarm.setStatus(1);
        vehicleAlarm.setResolveTime(LocalDateTime.now());
        vehicleAlarmMapper.updateById(vehicleAlarm);
    }

    @Override
    public void handleAlarmList(List<VehicleAlarm> para) {
//        List<VehicleAlarm> list = vehicleAlarmMapper.selectType(para);
        List<String> collect = para.stream().map(VehicleAlarm::getId).collect(Collectors.toList());
        List<VehicleAlarm> list = vehicleAlarmMapper.selectBatchIds(collect);
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).getStatus() == 1){
                throw new MyException("wds.exception.alarmContainHandle");
            }
            if (i < list.size()-1 && !list.get(i).getAlarmType().equals(list.get(i+1).getAlarmType())){
                throw new MyException("wds.exception.selectAlarm");
            }
        }
        List<VehicleAlarm> saveList = new ArrayList<>();
        for (VehicleAlarm alarm : para){
            VehicleAlarm vehicleAlarm = new VehicleAlarm();
            vehicleAlarm.setId(alarm.getId());
            vehicleAlarm.setHandler(alarm.getHandler());
            vehicleAlarm.setPhoneNumber(alarm.getPhoneNumber());
            vehicleAlarm.setAlarmReason(alarm.getAlarmReason());
            vehicleAlarm.setSuggestHandleMethod(alarm.getSuggestHandleMethod());
            vehicleAlarm.setRemark(alarm.getRemark());
            vehicleAlarm.setStatus(1);
            vehicleAlarm.setResolveTime(LocalDateTime.now());
            saveList.add(vehicleAlarm);
        }

        this.updateBatchById(saveList);


        /*for (VehicleAlarm alarm : para){
            LambdaUpdateWrapper<VehicleAlarm> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.eq(VehicleAlarm::getVin,alarm.getVin())
                    .eq(VehicleAlarm::getAlarmTime,alarm.getAlarmTime());

            VehicleAlarm vehicleAlarm = new VehicleAlarm();
            vehicleAlarm.setHandler(alarm.getHandler());
            vehicleAlarm.setPhoneNumber(alarm.getPhoneNumber());
            vehicleAlarm.setAlarmReason(alarm.getAlarmReason());
            vehicleAlarm.setSuggestHandleMethod(alarm.getSuggestHandleMethod());
            vehicleAlarm.setRemark(alarm.getRemark());
            vehicleAlarm.setStatus(1);
            vehicleAlarm.setResolveTime(LocalDateTime.now());
            vehicleAlarmMapper.update(vehicleAlarm,updateWrapper);
        }*/
    }

    @Override
    @LocalLock(key = "export",value = "tsp", expire = 60000)
    public void exportVehicleAlarm(QueryAlarm para, HttpServletResponse response) throws IOException {

        List<VehicleAlarm> data = vehicleAlarmMapper.exportVehicleAlarm(para);
        Long l = vehicleAlarmMapper.exportVehicleAlarmCount(para);
        Locale locale = LocaleContextHolder.getLocale();
        String fileName = "table.alarmDetail";
        String fileName1 = "table.alarm";
        String fileName2 = "table.handleAlarm";

        List<HandleAlarmExcel> list1 = new ArrayList<>();
        List<AlarmExcel> list2 = new ArrayList<>();
        for (VehicleAlarm vehicleAlarm : data){
            HandleAlarmExcel handleAlarmExcel = new HandleAlarmExcel();
            AlarmExcel alarmExcel = new AlarmExcel();
            if (vehicleAlarm.getStatus() == 1){
                BeanUtils.copyProperties(vehicleAlarm,handleAlarmExcel);
                handleAlarmExcel.setVin(StrUtil.hide(handleAlarmExcel.getVin(), 8, 11));
                handleAlarmExcel.setPhoneNumber(DesensitizedUtil.mobilePhone(handleAlarmExcel.getPhoneNumber()));
                if (StringUtil.isNotBlank(handleAlarmExcel.getLicenseNumber())){
                    if ("oil".equals(vehicleAlarm.getVehicleType())){
                        handleAlarmExcel.setLicenseNumber(StrUtil.hide(handleAlarmExcel.getLicenseNumber(), 3, 6));
                    }else {
                        handleAlarmExcel.setLicenseNumber(StrUtil.hide(handleAlarmExcel.getLicenseNumber(), 4, 7));
                    }
                }
                handleAlarmExcel.setAlarmType(String.valueOf(vehicleAlarm.getAlarmType().getValue()));
                list1.add(handleAlarmExcel);
            }else {
                BeanUtils.copyProperties(vehicleAlarm,alarmExcel);
                alarmExcel.setVin(StrUtil.hide(handleAlarmExcel.getVin(), 8, 11));
                if (StringUtil.isNotBlank(alarmExcel.getLicenseNumber())){
                    if ("oil".equals(vehicleAlarm.getVehicleType())){
                        alarmExcel.setLicenseNumber(StrUtil.hide(alarmExcel.getLicenseNumber(), 3, 6));
                    }else {
                        alarmExcel.setLicenseNumber(StrUtil.hide(alarmExcel.getLicenseNumber(), 4, 7));
                    }
                }
                alarmExcel.setAlarmType(String.valueOf(vehicleAlarm.getAlarmType().getValue()));
                list2.add(alarmExcel);
            }

        }

        fileName = messageSource.getMessage(fileName, null, fileName, locale);
        fileName1 = messageSource.getMessage(fileName1, null, fileName1, locale);
        fileName2 = messageSource.getMessage(fileName2, null, fileName2, locale);
        ExcelUtils.ExportWorkBook book = ExcelUtils.getExportWorkBook(fileName1, list2, AlarmExcel.class);
        ExcelUtils.ExportWorkBook exportWorkBook = ExcelUtils.addExportXSSFWorkbook(book, fileName2, list1, HandleAlarmExcel.class);
        Workbook xssfWorkbook = ExcelUtils.getExportXSSFWorkbook(exportWorkBook);
        ExcelUtils.export(response,fileName,xssfWorkbook);

    }

    @Override
    @LocalLock(key = "export",value = "tsp", expire = 60000)
    public void exportVehicleAlarm1(QueryAlarm para, HttpServletResponse response) throws IOException{
        /*int threadNum = Runtime.getRuntime().availableProcessors();
        System.out.println("系统最大线程数  ： "+threadNum);
        int total = vehicleAlarmMapper.exportAlarmList2();
        int pageSize = 5000;
        long start = System.currentTimeMillis();
        ExecutorService executorService = Executors.newFixedThreadPool(threadNum);
        List<Future<?>> futures = new ArrayList<>();
        for (int i = 0; i < threadNum; i++) {
            // 将循环变量 i 复制到 final 变量中
            final int currentThreadNum = i;
            futures.add(executorService.submit(() -> {
                int offset = currentThreadNum * pageSize;
                boolean hasMore = true;
                while (hasMore) {
                    if (offset > total) {
                        hasMore = false;
                    } else {
                        List<VehicleAlarm> dataList = vehicleAlarmMapper.exportAlarmList1(offset, pageSize);
                        dataList.clear();
                        offset += pageSize * threadNum;
                    }
                }
            }));
        }
        for (Future<?> future : futures) {
            try {
                future.get();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
        executorService.shutdown();
        long end = System.currentTimeMillis();
        System.out.println("==================================================");
        System.out.println("耗时：" + (end - start) + "ms");
        System.out.println("总数：" + total); //耗时：130683ms 总数：1315793*/

        long start = System.currentTimeMillis();
        int pageSize = 5000;
        String id = String.valueOf(0);
        boolean flag = true;
        long total = 0;
        while (flag) {
            List<VehicleAlarm> dataList = vehicleAlarmMapper.exportAlarmList3(id, pageSize);
            if (dataList.isEmpty()) {
                flag = false;
            }else {
                total += dataList.size();
                id = dataList.getLast().getId();
                dataList.clear();
            }
        }
        long end = System.currentTimeMillis();
        System.out.println("==================================================");
        System.out.println("耗时：" + (end - start) + "ms");
        System.out.println("总数：" + total);
        //耗时：28275ms

        /*int threadNum = Runtime.getRuntime().availableProcessors();
        System.out.println("系统最大线程数  ： "+threadNum);
        int pageSize = 5000;
        int totalCount = vehicleAlarmMapper.exportAlarmList2();
        AtomicInteger total = new AtomicInteger();

        long start = System.currentTimeMillis();
        ExecutorService executorService = Executors.newFixedThreadPool(threadNum);
        List<Future<?>> futures = new ArrayList<>();
        String[] strings = new String[threadNum];
        for (int i = 0; i < threadNum; i++) {
            // 将循环变量 i 复制到 final 变量中
            final int currentThreadNum = i;
            if (i == 0){
                strings[0] = String.valueOf(0);
            }else {
                strings[i] = vehicleAlarmMapper.exportAlarmList4((pageSize * i) - 1);
            }
            futures.add(executorService.submit(() -> {
                int j = 1;
                String currentId = strings[currentThreadNum];
                boolean flag = true;
                while (flag) {
                    List<VehicleAlarm> dataList = vehicleAlarmMapper.exportAlarmList3(currentId, pageSize);
                    if (dataList.isEmpty()) {
                        flag = false;
                    }else {
                        total.addAndGet(dataList.size());
                        //查询下一个的最小id，也就是本轮十个现场的最大值

                        int nextStart = (j * pageSize * threadNum - 1) + (currentThreadNum * pageSize);
                        currentId = vehicleAlarmMapper.exportAlarmList4(nextStart);
                        dataList.clear();
                        if (currentId == null) {
                            flag = false;
                        }
                    }
                    j ++;
                }
            }));
        }
        for (Future<?> future : futures) {
            try {
                future.get();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
        executorService.shutdown();
        long end = System.currentTimeMillis();
        System.out.println("==================================================");
        System.out.println("耗时：" + (end - start) + "ms");
        System.out.println("总数：" + total.get()); //耗时：30263ms 总数:1315793*/

        /*long start = System.currentTimeMillis();
        int threadNum = Runtime.getRuntime().availableProcessors() * 2;
        System.out.println("系统最大线程数  ： "+threadNum);
        int pageSize = 5000;
        int totalCount = vehicleAlarmMapper.exportAlarmList2();
        //计算每个线程查询数量
        int count = (int) Math.ceil((double) totalCount / threadNum);
        AtomicInteger total = new AtomicInteger();

        ExecutorService executorService = Executors.newFixedThreadPool(threadNum);
        List<Future<?>> futures = new ArrayList<>();
        String[] strings = new String[threadNum];
        for (int i = 0; i < threadNum; i++) {
            // 将循环变量 i 复制到 final 变量中
            final int currentThreadNum = i;

            futures.add(executorService.submit(() -> {
                if (currentThreadNum == 0){
                    strings[0] = String.valueOf(0);
                }else {
                    strings[currentThreadNum] = vehicleAlarmMapper.exportAlarmList4((count * currentThreadNum) - 1);
                }
                int j = 1;
                String currentId = strings[currentThreadNum];
                boolean flag = true;
                int queryCount = 0;
                int querySize = pageSize;
                if (count < pageSize){
                    querySize = count;
                }
                while (flag) {
                    List<VehicleAlarm> dataList = vehicleAlarmMapper.exportAlarmList3(currentId, querySize);
                    if (dataList.isEmpty()) {
                        flag = false;
                    }else {
                        queryCount += dataList.size();
                        total.addAndGet(dataList.size());
                        currentId = dataList.getLast().getId();
                        dataList.clear();
                        if (queryCount >= count){
                            flag = false;
                        }
                        if (queryCount + pageSize >= count){
                            querySize = count - queryCount;
                        }
                    }
                    j ++;
                }
            }));
        }
        for (Future<?> future : futures) {
            try {
                future.get();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
        executorService.shutdown();
        //正序排序
        long end = System.currentTimeMillis();
        System.out.println("==================================================");
        System.out.println("耗时：" + (end - start) + "ms");
        System.out.println("总数：" + total.get()); //8线程耗时：27262ms 总数：1315793  16线程耗时：27010ms*/


    }

    @Override
    public void exportAlarmByVin(String id, HttpServletResponse response) throws IOException {
        List<VinAlarmExcel> list = vehicleAlarmMapper.exportAlarmByVin(id);
        Locale locale = LocaleContextHolder.getLocale();
        String fileName = "table.vinAlarm";
        messageSource.getMessage(fileName,null,fileName,locale);
        ExcelUtils.export(response,fileName,list,VinAlarmExcel.class);
    }

    @Override
    public List<RealTimeAlarm> getRealAlarm() {
        List<RealTimeAlarm> realAlarm = vehicleAlarmMapper.getRealAlarm();
        realAlarm.forEach(a ->{
            a.setAlarmLevel(AlarmLevelEnum.getDesc(a.getLevel()));
            a.setAlarmType(AlarmTypeEnum.getDesc(Integer.valueOf(a.getAlarmType())));
            a.setVin(StrUtil.hide(a.getVin(), 8, 11));
        });
        return realAlarm;
    }

    @Override
    public List<VehicleBreakdown> selectBreakdowns() {
        return vehicleAlarmMapper.selectBreakdowns();
    }

    //国标十九项的报警统计
    @Override
    public List<AlarmList> getFaultAlarm() {
        return vehicleAlarmMapper.getFaultAlarm();
    }

    @Override
    public void exportAlarmList(QueryAlarmReport para, HttpServletResponse response) {
        Locale locale = LocaleContextHolder.getLocale();
        String fileName = "table.vehicle.alarm";
        List<VehicleAlarmReport> vehicleeAlarmList = vehicleAlarmMapper.exportAlarmList(para);
        for (VehicleAlarmReport vehicleAlarmReport : vehicleeAlarmList) {
            if(vehicleAlarmReport.getAlarmType() != null){
                String alarmType = vehicleAlarmReport.getAlarmType().getDesc();
                vehicleAlarmReport.setVehicleAlarmType(alarmType);
            }
        }
        fileName = messageSource.getMessage(fileName, null, fileName, locale);
        ExcelUtils.export(response,fileName,vehicleeAlarmList, VehicleAlarmReport.class);
    }

    @Override
    public TableDataInfo<VehicleAlarmReport> selectAlarmList(QueryAlarms para) {
        Page<VehicleAlarm> page = new Page<>();
        page.setCurrent(para.getPage());
        page.setSize(para.getPageSize());
        IPage<VehicleAlarmReport> data = vehicleAlarmMapper.selectAlarmList(page, para);

        return TableDataInfo.build(data);
    }

    @Override
    public void exportMailAlarmReportList() {
        Locale locale = LocaleContextHolder.getLocale();
        String fileName = "table.vehicle.offline";
        // 获取上个月的第一天和当前月份的第一天
        // 获取上个月的第一天和当前月份的第一天
        LocalDateTime firstDayOfLastMonth = YearMonth.now().minusMonths(1).atDay(1).atStartOfDay();
        LocalDateTime firstDayOfCurrentMonth = YearMonth.now().atDay(1).atStartOfDay();


        List<VehicleAlarmReport> vehicleeAlarmList = vehicleAlarmMapper.exportMailAlarmReportList(firstDayOfLastMonth, firstDayOfCurrentMonth);
        fileName = messageSource.getMessage(fileName, null, fileName, locale);
        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
            // 导出Excel文件到ByteArrayOutputStream
            ExcelUtils.export(outputStream, fileName, vehicleeAlarmList, VehicleAlarmReport.class);
            // 获取字节数组副本，用于后续邮件发送
            byte[] fileBytes = outputStream.toByteArray();
            // 异步发送邮件
            emailService.sendEmailAsync("车辆报警数据导出", "请查看附件中的车辆报警数据。", fileName + ".xlsx", fileBytes);
        } catch (IOException e) {
            log.error("导出Excel文件失败", e);
        }
    }
}